local msgserver = require "msgserver"
local crypt = require "skynet.crypt"
local skynet = require "skynet"

local loginservice
local watchdog 
local servername 

local CMD = {} 

local server = {}
local users = {} -- { uid : {fd, uid, username, agent ...} }
local internal_id = 0

-- login server disallow multi login, so login_handler never be reentry
-- call by login server
function server.login_handler(uid, secret)
	if users[uid] then
		error(string.format("%s is already login", uid))
	end

	internal_id = internal_id + 1
	local id = internal_id	-- don't use internal_id directly
	-- local username = msgserver.username(uid, id, servername)

	-- you can use a pool to alloc new agent
	local agent = skynet.call(watchdog, "lua", "alloc_agent", uid)
	if not agent then 
		logger.error("gated", "user uid", uid, "authed success, but login failed")
		error("init agent failed, maybe invalid login credential")
	end 

	local u = {
		uid = uid,
		subid = id,
		agent = agent,
	}

	-- trash subid (no used)
	-- skynet.call(agent, "lua", "login", uid, id, secret)

	users[uid] = u

	msgserver.login(uid, secret)

	-- you should return unique subid
	return id 
end

-- call by agent
function server.logout_handler(uid, subid)
	local u = users[uid]
	if u then
		-- local username = msgserver.username(uid, subid, servername)
		assert(u.uid == uid)
		msgserver.logout(u.uid)
		users[uid] = nil
		skynet.call(loginservice, "lua", "logout", uid, subid)
	end
end

-- call by login server
function server.kick_handler(uid, subid)
	local u = users[uid]
	if u then
		-- local username = msgserver.username(uid, subid, servername)
		assert(u.uid == uid)
		-- NOTICE: logout may call skynet.exit, so you should use pcall.
		pcall(skynet.call, u.agent, "lua", "logout")
	end
end

-- call by self (when socket disconnect)
function server.disconnect_handler(uid)
	local u = users[uid]
	if u then
		skynet.call(u.agent, "lua", "afk")
	end
end

-- call by self (when recv a request from client)
function server.request_handler(uid, msg, fd)
	local u = users[uid] 

	local agent = u.agent
	if agent then 
		skynet.redirect(agent, 0, "client", u.fd, msg)
	else	 
		skynet.send(watchdog, "lua", "socket", "data", fd, msg)
	end 
end

-- call by self (when gate open)
function server.register_handler(source, loginsrv, name)
	watchdog = source
	loginservice = loginsrv
	servername = name

	skynet.call(loginservice, "lua", "register_gate", servername, skynet.self())
end

-- 上层gate注册认证成功的句柄
-- 用于将对应客户端的agent绑定上必要的uid,fd,ip
function server.auth_handler(uid, fd, ip)
	local u = users[uid]
	u.fd = fd -- 对gate维护的用户绑定上fd

	local agent = u.agent 
	if agent then 
		skynet.call(watchdog, "lua", "auth_client_agent", agent, fd, ip)
	else
		-- 代理申请失败：
		users[uid] = nil 
		logger.error("gated", "fd", fd, "auth success but not found associated agent, ip", ip)
		-- error("auth succ, but client not associated with agent ~")
	end 
end 

function server.command_handler(cmd, source, ...)
	local f = assert(CMD[cmd])
	return f(source, ...)
end 


------------------------------------ CMD.FUNC --------------------------------



msgserver.start(server)

